iT邦幫忙

2026 iThome 鐵人賽

DAY 6
0
Kubernetes

探討k8s部署方式系列 第 6

Docker 淺談[Day6]

  • 分享至 

  • xImage
  •  

五十、為什麼需要 Docker?

前面提到,如果 Backend 是 Stateless,就可以很容易地增加或減少 Server:

Backend 1
Backend 2
Backend 3

但這時會遇到另一個問題:

怎麼確保每一台 Backend 的執行環境完全一樣?

假設 Backend 1 安裝:

Python 3.12
FastAPI
SQLAlchemy
MariaDB Client
Redis Client

Backend 2 卻是:

Python 3.11
不同版本 FastAPI
不同版本套件

那麼即使程式碼完全一樣,也可能出現:

Backend 1 → 正常

Backend 2 → 發生錯誤

因此部署時,不只是要部署「程式碼」,還要確保:

作業系統環境
Runtime
Python 版本
Package 版本
系統套件
設定
啟動方式

都一致。

Docker 就是為了解決這種「環境一致性」問題而非常常被使用。


五十一、傳統部署的問題

如果沒有 Docker,部署 FastAPI 常見流程可能是:

SSH 進入 Server
   │
   ▼
安裝 Python
   │
   ▼
建立 virtualenv
   │
   ▼
pip install requirements.txt
   │
   ▼
設定環境變數
   │
   ▼
啟動 Gunicorn / Uvicorn

例如:

python3 -m venv .venv

source .venv/bin/activate

pip install -r requirements.txt

gunicorn main:app

如果只有一台 Server,這樣通常沒什麼問題。

但如果現在有:

Backend 1
Backend 2
Backend 3
Backend 4
Backend 5

就必須確保五台機器全部安裝相同環境。

這會開始變得麻煩。

例如 Backend 4:

少裝了一個 library

Backend 5:

Python 版本不同

Backend 3:

OS Package 版本不同

於是就可能出現很經典的問題:

在我的機器上可以跑
為什麼 Server 上不行?

Docker 的目標之一,就是把:

Application
+
Runtime
+
Dependencies
+
Environment

包在一起。


五十二、什麼是 Docker Image?

Docker Image 可以理解成:

建立執行環境的模板。

例如 FastAPI 專案:

app/
├── main.py
├── requirements.txt
└── Dockerfile

Dockerfile 可能寫成:

FROM python:3.12-slim

WORKDIR /app

COPY requirements.txt .

RUN pip install --no-cache-dir -r requirements.txt

COPY . .

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

這份 Dockerfile 描述:

我要使用 Python 3.12

工作目錄是 /app

安裝 requirements.txt

把程式碼放進去

最後啟動 Uvicorn

接著執行:

docker build -t my-fastapi:1.0 .

Docker 就會建立:

my-fastapi:1.0

這就是 Docker Image。

可以把 Image 想成:

FastAPI Backend Template

五十三、Image 跟 Container 有什麼差別?

這是 Docker 最重要的概念之一。

可以簡單理解成:

Image
= 模板

Container
= 根據模板實際跑起來的 Instance

例如:

my-fastapi:1.0

是一個 Image。

可以從這個 Image 啟動:

Container 1
Container 2
Container 3

例如:

docker run my-fastapi:1.0

就是建立一個 Container。

因此:

                  Image

             my-fastapi:1.0

                  │
        ┌─────────┼─────────┐
        ▼         ▼         ▼
   Container 1 Container 2 Container 3

三個 Container 使用的是同一個 Image。

所以它們的:

Python Version
Package Version
Application Code
Startup Command

理論上都是一樣的。


五十四、Docker 對水平擴充的幫助

前面提到 Horizontal Scaling:

Backend 1
Backend 2
Backend 3

如果使用 Docker,就可以變成:

                Docker Image
                backend:v1.0
                     │
          ┌──────────┼──────────┐
          ▼          ▼          ▼
     Container 1 Container 2 Container 3

每個 Container 都運行相同的 FastAPI。

Load Balancer 可以分配:

Request 1 → Container 1
Request 2 → Container 2
Request 3 → Container 3

如果流量增加:

Container 4
Container 5

只需要繼續使用同一個 Image 啟動新的 Container。

因此:

Stateless Backend
      +
Docker Image
      │
      ▼
Backend 可以快速複製

這就是 Docker 和 Auto Scaling 之間很重要的關係。


五十五、Container 與虛擬機有什麼不同?

Docker Container 不是完整的 Virtual Machine。

傳統 VM:

Physical Server
     │
 Hypervisor
     │
 ┌───┴────────────┐
 │                │
VM 1             VM 2
│                │
Guest OS         Guest OS
Python           Python
FastAPI          FastAPI

每一個 VM 通常都有自己的完整 Guest OS。

Container 則是:

Physical Server
     │
 Host OS
     │
 Docker Engine
     │
 ┌───┼───┐
 ▼   ▼   ▼
C1  C2  C3

Container 共享 Host 的 Kernel,但各自擁有隔離的:

Filesystem
Process
Network
Environment

因此 Container 通常比 VM 更輕量,也能更快建立。


五十六、Docker 如何讓不同環境一致?

假設開發環境:

Developer Mac

測試環境:

Staging Linux Server

Production:

Production Linux Server

如果都是直接安裝環境:

Developer
Python 3.12.1

Staging
Python 3.12.3

Production
Python 3.11

可能產生差異。

如果改成:

backend:v1.0

同一個 Image 部署到:

Developer
Staging
Production

就能大幅降低環境差異。

概念變成:

Code
  │
  ▼
Docker Build
  │
  ▼
backend:v1.0
  │
  ├── Development
  ├── Staging
  └── Production

而不是每個環境都重新安裝一遍。


五十七、什麼是 Container Registry?

如果 Image 只存在自己的電腦,就無法讓其他 Server 使用。

因此通常需要一個地方保存 Image。

這就是:

Container Registry

常見服務包括:

Docker Hub
GitHub Container Registry
Amazon ECR
Google Artifact Registry
Azure Container Registry

整個流程可以變成:

Developer
   │
   ▼
Git Push
   │
   ▼
CI/CD
   │
   ▼
Docker Build
   │
   ▼
backend:v1.0
   │
   ▼
Container Registry

接著 Production Server:

Production Server
   │
   ▼
docker pull backend:v1.0
   │
   ▼
docker run

因此 Registry 可以理解成:

Docker Image 的倉庫

五十八、Docker 與 CI/CD 的關係

加入 Docker 之後,部署流程可以變得更一致。

例如:

Developer
   │
   │ git push
   ▼
Git Repository
   │
   ▼
CI Pipeline
   │
   ├── Run Tests
   ├── Build Docker Image
   └── Push Image
           │
           ▼
      Container Registry
           │
           ▼
      Production Server
           │
           ▼
      Pull New Image
           │
           ▼
      Start Container

例如版本:

backend:v1.0
backend:v1.1
backend:v1.2

如果新版出問題,也比較容易回到:

backend:v1.1

這就是 Image Versioning 帶來的另一個好處。


五十九、Environment Variable 不應該寫死在 Image 裡

雖然程式碼與 Runtime 可以包進 Image,但不同環境的設定通常不應直接寫死。

例如:

Database Password
Redis Host
JWT Secret
API Key
Environment

Development:

DB_HOST=dev-db

Staging:

DB_HOST=staging-db

Production:

DB_HOST=prod-db

但是三個環境仍然可以使用:

backend:v1.0

也就是:

相同 Image
+
不同 Configuration

例如:

docker run \
  -e DB_HOST=prod-db \
  -e REDIS_HOST=prod-redis \
  backend:v1.0

這樣 Image 本身不需要為每個環境重新修改程式碼。


六十、Docker Volume

Container 還有一個很重要的特性:

Container 本身應該被視為可以被刪除的。

例如:

Container 1
   │
   X
刪除

如果重要資料只存在 Container 內部,也可能一起消失。

因此像 Database:

MySQL
MariaDB

通常需要使用 Volume 或外部 Storage。

例如:

MariaDB Container
       │
       ▼
    Volume
       │
       ▼
 /data/mysql

Container 即使被刪除:

MariaDB Container X

資料還可以保留在 Volume。

這與前面 Stateless Backend 的概念非常相似。

Backend Container:

可以被刪除
可以被重建

重要資料則放在外部:

Database
Redis
Object Storage
Volume

六十一、Docker Compose

如果開發環境裡不只有 FastAPI,而是:

FastAPI
Redis
MariaDB

可以使用 Docker Compose 管理多個 Container。

例如:

services:

  backend:
    build: .
    ports:
      - "8000:8000"
    depends_on:
      - redis
      - db

  redis:
    image: redis:7

  db:
    image: mariadb:11

然後:

docker compose up

就可以一次啟動:

Backend Container
Redis Container
MariaDB Container

架構:

             Docker Compose

       ┌─────────┼─────────┐
       ▼         ▼         ▼
   FastAPI     Redis     MariaDB

Docker Compose 對:

Local Development
POC
測試環境
小型部署

非常方便。


六十二、Docker 解決的是什麼問題?

Docker 並不是單純:

把程式放進 Container

它真正解決的是:

如何把 Application 與執行環境一起標準化。

從:

我要 SSH 到每台 Server
然後安裝 Python
然後安裝 Package
然後設定服務

變成:

Build 一次 Image

然後:

Run Everywhere

因此部署單位從:

Source Code

逐漸變成:

Container Image

這是一個非常重要的改變。


六十三、加入 Docker 後的整體架構

現在前面的架構可以變成:

                         Internet
                            │
                            ▼
                      Load Balancer
                            │
               ┌────────────┼────────────┐
               ▼            ▼            ▼
           Container 1  Container 2  Container 3
             FastAPI      FastAPI      FastAPI
               │            │            │
               └──────┬─────┴─────┬──────┘
                      │           │
                      ▼           ▼
                    Redis       Database

而三個 Container 都來自:

backend:v1.0

因此:

               backend:v1.0
                    │
          ┌─────────┼─────────┐
          ▼         ▼         ▼
       API 1      API 2      API 3

這讓部署、擴充與替換 Backend 都更加一致。


六十四、Docker 還沒有解決什麼?

Docker 解決了:

如何建立一致的 Container

但如果系統有:

100 個 Container

新的問題就會出現:

哪台 Server 要跑哪些 Container?

Container 掛掉誰負責重啟?

Load Balancer 要怎麼知道新 Container?

CPU 太高怎麼自動增加 Container?

部署新版時怎麼逐步替換?

某台 Server 掛掉怎麼把 Container 搬走?

例如:

Server A
├── Container 1
├── Container 2
└── Container 3

Server B
├── Container 4
├── Container 5
└── Container 6

Server C
├── Container 7
├── Container 8
└── Container 9

如果全部靠人工:

SSH Server A
docker run ...

SSH Server B
docker run ...

SSH Server C
docker run ...

系統規模一大又會變得難以管理。

因此下一個問題就變成:

誰來自動管理大量 Container?

這類系統稱為:

Container Orchestration

而目前最常見的代表之一,就是:

Kubernetes。


六十五、部署架構演進到 Docker

到目前為止,可以整理成:

單機部署
    │
    ▼
前後端分離
    │
    ▼
Load Balancer
    │
    ▼
多台 Backend
    │
    ▼
Redis / Shared State
    │
    ▼
Stateless Backend
    │
    ▼
Auto Scaling
    │
    ▼
Docker Image
    │
    ▼
Container
    │
    ▼
Container Registry
    │
    ▼
大量 Container 管理
    │
    ▼
Kubernetes

因此 Docker 並不是獨立存在的一個工具。

放在整個部署架構中來看,它解決的是:

如何把 Stateless Backend 標準化成一個可以快速建立、複製、部署與替換的執行單位。

而 Kubernetes 接下來要解決的,就是:

當這些 Container 數量變多之後,如何自動管理它們的生命週期。


上一篇
從快取延伸至無狀態後端[Day5]
下一篇
多主機與多容器遇到的實際問題與K8S[Day7]
系列文
探討k8s部署方式9
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言